#region Imports

using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.SharePoint;

#endregion

namespace IEC.ColumnOrdering.SP2007.Deployment
{

    /// <summary>
    /// Provides helper functionality for SharePoint setup operations.
    /// </summary>
    public static class SPSetupHelper
    {

        #region Constants

        /// <summary>
        /// The maximum number of characters allowed in a group name
        /// </summary>
        private const int GroupNameMaximumLength = 255;

        #region Constants - Query Format Strings

        /// <summary>
        /// The query text for GetLookupValueItemByTitle.
        /// </summary>
        private const string GetLookupValueItemByTitleQueryFormat =
            @"<Where>
                <Eq>
                  <FieldRef Name=""Title"" />
                  <Value Type=""Text"">{0}</Value>
                </Eq>
              </Where>";

        #endregion

        #endregion

        #region Methods - Static Member

        #region Methods - Static Member - (content type setup)

        /// <summary>
        /// Creates the content type based on the specified content type name,
        /// parent content type name and field names.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="groupName">
        /// The group under which the content type is to be created.
        /// </param>
        /// <param name="contentTypeName">
        /// The name of the content type.
        /// </param>
        /// <param name="parentContentTypeName">
        /// The name of the parent content type.
        /// </param>
        /// <param name="fieldSpecifications">
        /// The names of the fields to be created in the content type identified by
        /// <paramref name="contentTypeName"/>.
        /// </param>
        public static void CreateContentType(
            SPWeb web,
            string groupName,
            string contentTypeName,
            string parentContentTypeName,
            IList<Field> fieldSpecifications)
        {
            if (!(SPSetupHelper.ContentTypeExists(web, contentTypeName)))
            {
                // get the parent content type
                SPContentType parentContentType =
                    web.ContentTypes[parentContentTypeName];

                // create the new content type
                SPContentType contentType = new SPContentType(
                    parentContentType,
                    web.ContentTypes,
                    contentTypeName);

                SPFieldLinkCollection fieldLinks = contentType.FieldLinks;
                SPFieldCollection fields = web.Fields;

                foreach (Field fieldSpecification in fieldSpecifications)
                {
                    SPField field = fields[fieldSpecification.Name];

                    LookupField lookupFieldSpecification = fieldSpecification as LookupField;

                    if (lookupFieldSpecification != null)
                    {
                        contentType =
                            SPSetupHelper.AddContentTypeLookupField(
                            web, contentType, fieldSpecification.Name);
                    }
                    else
                    {
                        SPFieldLink fieldLink = new SPFieldLink(field);
                        fieldLinks.Add(fieldLink);
                    }
                }

                // add the content type to the content type
                web.ContentTypes.Add(contentType);

                // update the content type
                contentType.Group = groupName;
                contentType.Update();
            }
        }

        /// <summary>
        /// Creates the content type based on the specified content type name,
        /// parent content type name and field names.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="groupName">
        /// The group under which the content type is to be created.
        /// </param>
        /// <param name="contentTypeName">
        /// The name of the content type.
        /// </param>
        /// <param name="parentContentTypeName">
        /// The name of the parent content type.
        /// </param>
        /// <param name="fieldNames">
        /// The names of the fields to be created in the content type identified by
        /// <paramref name="contentTypeName"/>.
        /// </param>
        public static void CreateContentType(
            SPWeb web,
            string groupName,
            string contentTypeName,
            string parentContentTypeName,
            IList<string> fieldNames)
        {
            IList<Field> fieldSpecifications = new List<Field>();

            foreach (string fieldName in fieldNames)
            {
                fieldSpecifications.Add(new Field(fieldName));
            }

            SPSetupHelper.CreateContentType(
                web, groupName, contentTypeName, parentContentTypeName, fieldSpecifications);
        }

        /// <summary>
        /// Determines whether the content type exists.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="contentTypeName">
        /// The name of the content type.
        /// </param>
        /// <returns>
        /// <c>true</c> if the content type exists;
        /// otherwise, <c>false</c>.
        /// </returns>
        public static bool ContentTypeExists(SPWeb web, string contentTypeName)
        {
            bool exists = false;

            foreach (SPContentType contentType in web.ContentTypes)
            {
                if (contentType.Name == contentTypeName)
                {
                    exists = true;
                    break;
                }
            }

            return exists;
        }

        /// <summary>
        /// Adds the content type lookup field.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="contentType">
        /// Type of the content.
        /// </param>
        /// <param name="lookupFieldName">
        /// Name of the lookup field.
        /// </param>
        /// <returns>
        /// Content type with the lookup field
        /// </returns>
        public static SPContentType AddContentTypeLookupField(
            SPWeb web,
            SPContentType contentType,
            string lookupFieldName)
        {
            // Get the lookup field
            SPFieldLookup lookupField = (SPFieldLookup)web.Fields[lookupFieldName];

            // Create a new SPFieldLink
            SPFieldLink lookupFieldLink = new SPFieldLink(lookupField);

            // Add the SPFieldLink
            contentType.FieldLinks.Add(lookupFieldLink);

            return contentType;
        }

        #endregion

        #region Methods - Static Member - (list setup)

        /// <summary>
        /// Creates the lists for the given SharePoint website.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// The name of the list.
        /// </param>
        /// <param name="displayOnQuickLaunch">
        /// if set to <c>true</c> [display on quick launch].
        /// </param>
        public static void CreateList(SPWeb web, string listName, bool displayOnQuickLaunch)
        {
            if (!(SPSetupHelper.ListExists(web, listName)))
            {
                SPSetupHelper.CreateList(web, listName);

                SPList currentList = web.Lists[listName];

                // Display on the quick launch
                currentList.OnQuickLaunch = displayOnQuickLaunch;

                currentList.Update();
            }
        }

        /// <summary>
        /// Creates the lists for the given SharePoint website.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// The name of the list.
        /// </param>
        public static void CreateList(SPWeb web, string listName)
        {
            if (!(SPSetupHelper.ListExists(web, listName)))
            {
                SPSetupManager setupLibrary = new SPSetupManager();

                // Create the list
                setupLibrary.CreateList(web, listName);
                SPList currentList = web.Lists[listName];

                // Enable versioning
                currentList.EnableVersioning = true;

                // Disable attachments to list items
                currentList.EnableAttachments = false;

                // Enable allow management of content types
                currentList.ContentTypesEnabled = true;
                currentList.Update();
            }
        }

        /// <summary>
        /// Determines whether the list exists in the given website.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// The name of the list.
        /// </param>
        /// <returns>
        /// <c>true</c> if the list exists;
        /// otherwise, <c>false</c>.
        /// </returns>
        public static bool ListExists(SPWeb web, string listName)
        {
            SPSetupManager setupLibrary = new SPSetupManager();

            return setupLibrary.ListExists(web, listName);
        }

        /// <summary>
        /// Creates the list column.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// Name of the list.
        /// </param>
        /// <param name="fieldName">
        /// Name of the field.
        /// </param>
        /// <param name="fieldType">
        /// Type of the field.
        /// </param>
        /// <param name="isRequired">
        /// <c>true</c> if required
        /// otherwise, <c>false</c>.
        /// </param>
        public static void CreateListColumn(
            SPWeb web, string listName, string fieldName, SPFieldType fieldType, bool isRequired)
        {
            SPList list = web.Lists[listName];

            if (!(SPSetupHelper.FieldExists(list, fieldName)))
            {
                list.Fields.Add(fieldName, fieldType, isRequired);
                SPSetupHelper.AddFieldToView(list, fieldName);
            }
        }

        /// <summary>
        /// Creates the multi line column.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// Name of the list.
        /// </param>
        /// <param name="fieldName">
        /// Name of the field.
        /// </param>
        /// <param name="isRequired">
        /// <c>true</c> if required
        /// otherwise, <c>false</c>.
        /// </param>
        public static void CreateMultilineColumn(
            SPWeb web, string listName, string fieldName, bool isRequired)
        {
            SPList list = web.Lists[listName];

            if (!(SPSetupHelper.FieldExists(list, fieldName)))
            {
                list.Fields.Add(fieldName, SPFieldType.Note, isRequired);
                SPFieldMultiLineText field = list.Fields[fieldName] as SPFieldMultiLineText;
                field.RichText = true;
                field.AppendOnly = true;
                field.Update(true);
            }
        }

        /// <summary>
        /// Creates the calculated column.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// Name of the list.
        /// </param>
        /// <param name="fieldName">
        /// Name of the field.
        /// </param>
        /// <param name="isRequired">
        /// <c>true</c> if required
        /// otherwise, <c>false</c>.
        /// </param>
        /// <param name="formula">
        /// Formula of the calculated field.
        /// </param>
        /// <param name="outputType">
        /// Field type of output type.
        /// </param>
        public static void CreateCalculatedColumn(
            SPWeb web,
            string listName,
            string fieldName,
            bool isRequired,
            string formula,
            SPFieldType outputType)
        {
            SPList list = web.Lists[listName];

            if (!(SPSetupHelper.FieldExists(list, fieldName)))
            {
                list.Fields.Add(fieldName, SPFieldType.Calculated, isRequired);
                SPFieldCalculated field = list.Fields[fieldName] as SPFieldCalculated;
                field.Formula = formula;
                field.OutputType = outputType;
                field.Update(true);

                SPSetupHelper.AddFieldToView(list, fieldName);
            }
        }

        /// <summary>
        /// Creates the list lookup column with single selection.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// Name of the list.
        /// </param>
        /// <param name="fieldName">
        /// Name of the field.
        /// </param>
        /// <param name="isRequired">
        /// <c>true</c> if required
        /// otherwise, <c>false</c>.
        /// </param>
        /// <param name="lookupListName">
        /// Name of the lookup list.
        /// </param>
        public static void CreateListLookupColumn(
            SPWeb web,
            string listName,
            string fieldName,
            bool isRequired,
            string lookupListName)
        {
            SPSetupHelper.CreateListLookupColumn(
                web,
                listName,
                fieldName,
                isRequired,
                lookupListName,
                false);
        }

        /// <summary>
        /// Creates the list lookup column with multiple selection.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// Name of the list.
        /// </param>
        /// <param name="fieldName">
        /// Name of the field.
        /// </param>
        /// <param name="isRequired">
        /// <c>true</c> if required
        /// otherwise, <c>false</c>.
        /// </param>
        /// <param name="lookupListName">
        /// Name of the lookup list.
        /// </param>
        /// <param name="isMultipleSelect">
        /// <c>true</c> if multiselect
        /// otherwise, <c>false</c>.
        /// </param>
        public static void CreateListLookupColumn(
            SPWeb web,
            string listName,
            string fieldName,
            bool isRequired,
            string lookupListName,
            bool isMultipleSelect)
        {
            SPList list = web.Lists[listName];
            SPList parentList = web.Lists[lookupListName];

            if (!(SPSetupHelper.FieldExists(list, fieldName)))
            {
                list.Fields.AddLookup(fieldName, parentList.ID, isRequired);
                SPSetupHelper.AddFieldToView(list, fieldName);
            }

            SPFieldLookup lookupField = (SPFieldLookup)list.Fields[fieldName];
            lookupField.AllowMultipleValues = isMultipleSelect;
        }

        /// <summary>
        /// Creates the site column.
        /// </summary>
        /// <param name="currentWeb">
        /// The current web.
        /// </param>
        /// <param name="lookupList">
        /// The lookup list.
        /// </param>
        /// <param name="lookupColumn">
        /// The lookup column.
        /// </param>
        /// <param name="isMultiple">
        /// <c>true</c> if multiselect
        /// otherwise, <c>false</c>.
        /// </param>
        public static void CreateSiteColumn(
            SPWeb currentWeb,
            string lookupList,
            string lookupColumn,
            bool isMultiple)
        {
            SPSetupManager setupLibrary = new SPSetupManager();

            if (!setupLibrary.SiteColumnExists(currentWeb, lookupColumn))
            {
                SPList list = currentWeb.Lists[lookupList];
                string fieldName =
                    currentWeb.Fields.AddLookup(lookupColumn, list.ID, false);
                SPFieldLookup currentField =
                  (SPFieldLookup)currentWeb.Fields.GetFieldByInternalName(fieldName);
                currentField.AllowMultipleValues = isMultiple;
                currentField.Update();
            }
        }

        /// <summary>
        /// Determines whether the specified field exists in the given list.
        /// </summary>
        /// <param name="list">
        /// The name of the list to be checked in.
        /// </param>
        /// <param name="fieldName">
        /// The name of the field to be checked for, in <paramref name="list"/>.
        /// </param>
        /// <returns>
        /// <c>true</c> if the field exists;
        /// otherwise, <c>false</c>.
        /// </returns>
        public static bool FieldExists(SPList list, string fieldName)
        {
            bool isFieldExist = false;

            foreach (SPField field in list.Fields)
            {
                if (field.Title == fieldName)
                {
                    isFieldExist = true;

                    break;
                }
            }

            return isFieldExist;
        }

        /// <summary>
        /// Adds a field to the default view of a list.
        /// </summary>
        /// <param name="list">The list to which the field should be added.</param>
        /// <param name="fieldName">The name of the field to add.</param>
        public static void AddFieldToView(SPList list, string fieldName)
        {
            SPView defaultView = list.DefaultView;

            defaultView.ViewFields.Add(fieldName);
            defaultView.Update();
        }

        /// <summary>
        /// Creates a list item in the list with the specified name, 
        /// within the given website, using the specified indexed data.
        /// </summary>
        /// <param name="web">
        /// The SharePoint website within which the setup operation is to be performed.
        /// </param>
        /// <param name="listName">
        /// The name of the list to be populated.
        /// </param>
        /// <param name="data">
        /// The indexed data to be added to the list.
        /// </param>
        public static void CreateListItem(
            SPWeb web, string listName, IDictionary<string, object> data)
        {
            SPList list = web.Lists[listName];

            SPSetupHelper.CreateListItem(list, data);
        }

        /// <summary>
        /// Creates a list item in the specified list, using the 
        /// specified indexed data.
        /// </summary>
        /// <param name="list">
        /// The list to be populated.
        /// </param>
        /// <param name="data">
        /// The indexed data to be added to the list.
        /// </param>
        public static void CreateListItem(SPList list, IDictionary<string, object> data)
        {
            bool isListItemExists = false;
            if (list != null)
            {

                isListItemExists = ListItemExists(list, data);

                if (!isListItemExists)
                {
                    SPListItem listItem = list.Items.Add();

                    foreach (string key in data.Keys)
                    {
                        SPFieldLookup fieldLookup = list.Fields[key] as SPFieldLookup;

                        if (fieldLookup != null)
                        {
                            Guid parentListGuid = new Guid(fieldLookup.LookupList);
                            SPList parentList = list.ParentWeb.Lists[parentListGuid];

                            // Handle setting a lookup field value
                            SPFieldLookupValue lookupValue =
                                SPSetupHelper.GetFieldLookupValueByTitle(
                                list.ParentWeb, parentList.Title, (string)data[key]);

                            if (lookupValue != null)
                            {
                                listItem[key] = lookupValue;
                            }
                        }
                        else
                        {
                            // Handle setting a simple SP field value
                            listItem[key] = data[key];
                        }
                    }

                    listItem.Update();
                }
            }
        }

        /// <summary>
        /// Lists the item exists.
        /// </summary>
        /// <param name="list">
        /// The list has to be checked.
        /// </param>
        /// <param name="data">
        /// The indexed data to be added to the list.
        /// </param>
        /// <returns>
        /// <c>true</c> if the list item exists;
        /// otherwise, <c>false</c>.
        /// </returns>
        public static bool ListItemExists(SPList list, IDictionary<string, object> data)
        {
            bool isListItemExist = false;

            foreach (SPListItem listItem in list.Items)
            {
                bool areAllFieldsEqual = false;
                bool isFieldNotEqual = false;

                foreach (string key in data.Keys)
                {
                    isFieldNotEqual = SPSetupHelper.AreFieldsEqual(list, data, listItem, key);

                    if (!(isFieldNotEqual))
                    {
                        areAllFieldsEqual = true;

                        break;
                    }
                    else
                    {
                        break;
                    }
                }

                if (areAllFieldsEqual)
                {
                    isListItemExist = true;
                    break;
                }
            }

            return isListItemExist;
        }

        /// <summary>
        /// Checks wether the fields are equal
        /// </summary>
        /// <param name="list">
        /// The list has to be checked.
        /// </param>
        /// <param name="data">
        /// The indexed data to be added to the list.
        /// </param>
        /// <param name="listItem">
        /// The list item.
        /// </param>
        /// <param name="key">
        /// The key value.
        /// </param>
        /// <returns>
        /// <c>true</c> if the list item exists;
        /// otherwise, <c>false</c>.
        /// </returns>
        private static bool AreFieldsEqual(
            SPList list, IDictionary<string, object> data, SPListItem listItem, string key)
        {
            bool isFieldNotEqual = false;

            SPFieldLookup fieldLookup = list.Fields[key] as SPFieldLookup;

            if (fieldLookup != null)
            {
                Guid parentListGuid = new Guid(fieldLookup.LookupList);
                SPList parentList = list.ParentWeb.Lists[parentListGuid];

                SPFieldLookupValue lookupValue = SPSetupHelper.GetFieldLookupValueByTitle(
                    list.ParentWeb, parentList.Title, (string)data[key]);

                if (lookupValue != null)
                {
                    if (listItem[key] != lookupValue)
                    {
                        isFieldNotEqual = true;
                    }
                }
            }
            else
            {
                if (!(object.Equals(listItem[key], data[key])))
                {
                    isFieldNotEqual = true;
                }
            }
            return isFieldNotEqual;
        }

        /// <summary>
        /// Gets the field lookup value by title.
        /// </summary>
        /// <param name="web">
        /// The name of the web containing the list to populate.
        /// </param>
        /// <param name="listName">
        /// Name of the list.
        /// </param>
        /// <param name="columnValue">
        /// The column value.
        /// </param>
        /// <returns>
        /// An <see cref="SPFieldLookupValue"/> representation of the lookup value.
        /// </returns>
        public static SPFieldLookupValue GetFieldLookupValueByTitle(
            SPWeb web,
            string listName,
            string columnValue)
        {
            SPFieldLookupValue lookupValue = null;

            SPList list = web.Lists[listName];

            SPQuery query = new SPQuery();
            query.Query = string.Format(
                CultureInfo.CurrentUICulture,
                SPSetupHelper.GetLookupValueItemByTitleQueryFormat,
                columnValue);

            SPListItemCollection listItems = list.GetItems(query);

            if (listItems.Count > 0)
            {
                lookupValue = new SPFieldLookupValue(listItems[0].ID, listItems[0].Title);
            }

            return lookupValue;
        }

        #endregion

        #region Methods - Static Member - (group setup)

        /// <summary>
        /// Creates a group for the given SharePoint site collection.
        /// </summary>
        /// <param name="site">
        /// The SharePoint site collection within which the setup operation is to be performed.
        /// </param>
        /// <param name="name">
        /// The name of the group.
        /// </param>
        /// <param name="description">
        /// The description for the group.
        /// </param>
        public static void CreateGroup(SPSite site, string name, string description)
        {
            if (!(SPSetupHelper.GroupExists(site, name)))
            {
                site.RootWeb.SiteGroups.Add(
                    name,
                    site.RootWeb.AssociatedOwnerGroup,
                    null,
                    description);
            }
        }

        /// <summary>
        /// Determines whether the group exists in the given site.
        /// </summary>
        /// <param name="site">
        /// The SharePoint site collection within which the setup operation is to be performed.
        /// </param>
        /// <param name="name">
        /// The name of the group.
        /// </param>
        /// <returns>
        /// <c>true</c> if the group exists;
        /// otherwise, <c>false</c>.
        /// </returns>
        private static bool GroupExists(SPSite site, string name)
        {
            bool result = false;
            SPGroupCollection groups = site.RootWeb.SiteGroups;

            if (!(string.IsNullOrEmpty(name) ||
                (name.Length > GroupNameMaximumLength) ||
                (groups == null) ||
                (groups.Count == 0)))
            {
                // convert the group name to a string array
                string[] groupCollection = new string[] { name };

                // get the collection of SPGroup objects matching the name
                SPGroupCollection namedGroups = groups.GetCollection(groupCollection);
                result = (namedGroups.Count > 0);
            }

            return result;
        }

        #endregion

        #region Methods - Static Member - (permission level setup)

        /// <summary>
        /// Roles the definition exists.
        /// </summary>
        /// <param name="currentWeb">
        /// The current web.
        /// </param>
        /// <param name="roleName">
        /// Name of the role.
        /// </param>
        /// <returns>
        /// <c>true</c> if the permission level exists;
        /// otherwise, <c>false</c>.
        /// </returns>
        public static bool RoleDefinitionExists(SPWeb currentWeb, string roleName)
        {
            bool exists = false;
            foreach (SPRoleDefinition roleDefinition in currentWeb.RoleDefinitions)
            {
                if (roleDefinition.Name == roleName)
                {
                    exists = true;
                }
            }
            return exists;
        }

        #endregion

        #endregion

    }

}
